import { ChatPromptTemplate, PromptTemplate } from '@langchain/core/prompts'
import { StringOutputParser } from '@langchain/core/output_parsers'
import { RunnableSequence } from '@langchain/core/runnables'
import { BaseMessage, HumanMessage, SystemMessage } from '@langchain/core/messages'
import Store from 'electron-store'
import { config } from 'dotenv'
import {
  AIProvider,
  AIProviderManager,
  ProviderConfig,
  AIRequestOptions,
  AIStreamOptions
} from './AIProviderAdapter'
import { getTokenManager } from './TokenManager'
import { CacheManager, getAICache } from './CacheManager'
import { getSecurityService } from './SecurityService'
import { log } from '../utils/Logger'

// Load environment variables
config()

// Re-export AIProvider from adapter
export { AIProvider } from './AIProviderAdapter'

export interface AIAction {
  polish: (text: string) => Promise<string>
  shorten: (text: string) => Promise<string>
  expand: (text: string) => Promise<string>
  translate: (text: string, targetLang: string) => Promise<string>
  fixGrammar: (text: string) => Promise<string>
  changeTone: (text: string, tone: string) => Promise<string>
  summarize: (text: string) => Promise<string>
  continueWriting: (text: string, context: string) => Promise<string>
}

export interface LangChainConfig {
  provider: AIProvider
  apiKey?: string
  model?: string
  temperature?: number
  maxTokens?: number
  baseURL?: string
  timeout?: number
  retries?: number
}

// Re-export from adapter
export { AIRequestOptions, AIStreamOptions } from './AIProviderAdapter'

export class LangChainService implements AIAction {
  private config: LangChainConfig
  private store: Store
  private providerManager: AIProviderManager
  private tokenManager = getTokenManager()
  private cache = getAICache()
  private securityService = getSecurityService()
  private chains: Map<string, RunnableSequence<any, any>> = new Map()
  private isInitialized = false

  constructor() {
    this.store = new Store()
    this.config = this.loadConfig()
    this.providerManager = new AIProviderManager()
    this.initialize()
  }

  private loadConfig(): LangChainConfig {
    // Load from environment variables first, then fallback to store
    const envConfig: Partial<LangChainConfig> = {
      provider: (process.env.AI_PROVIDER || this.store.get('ai.provider', 'openai')) as AIProvider,
      apiKey: process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.DEEPSEEK_API_KEY || this.store.get('ai.apiKey') as string,
      model: process.env.AI_MODEL || this.store.get('ai.model') as string,
      temperature: parseFloat(process.env.AI_TEMPERATURE || String(this.store.get('ai.temperature', 0.7))),
      maxTokens: parseInt(process.env.AI_MAX_TOKENS || String(this.store.get('ai.maxTokens', 2000))),
      baseURL: process.env.AI_BASE_URL || this.store.get('ai.baseURL') as string,
      timeout: parseInt(process.env.AI_TIMEOUT || String(this.store.get('ai.timeout', 60000))),
      retries: parseInt(process.env.AI_RETRIES || String(this.store.get('ai.retries', 3)))
    }

    return {
      provider: 'openai',
      apiKey: '',
      model: '',
      temperature: 0.7,
      maxTokens: 2000,
      ...envConfig
    }
  }

  private async initialize(): Promise<void> {
    if (this.isInitialized) return

    try {
      // Try to load API key from secure storage
      if (!this.config.apiKey) {
        this.config.apiKey = await this.getSecureApiKey() || ''
      }

      await this.initializeProviders()
      this.initializeChains()
      this.isInitialized = true
      log.ai(`LangChainService initialized with provider: ${this.config.provider}`)
    } catch (error) {
      log.error('Failed to initialize LangChainService:', error, 'AI')
      throw error
    }
  }

  private async initializeProviders(): Promise<void> {
    const { provider, apiKey, model, temperature, maxTokens, baseURL, timeout, retries } = this.config

    if (!apiKey && provider !== 'local') {
      console.warn(`${provider} API key not configured`)
      return
    }

    const providerConfig: ProviderConfig = {
      apiKey: apiKey || '',
      model: model || this.getDefaultModel(provider),
      temperature: temperature || 0.7,
      maxTokens: maxTokens || 2000,
      baseURL,
      timeout: timeout || 60000,
      retries: retries || 3,
    }

    // Import adapter classes
    const {
      OpenAIProviderAdapter,
      AnthropicProviderAdapter,
      DeepSeekProviderAdapter,
      LocalProviderAdapter
    } = await import('./AIProviderAdapter')

    switch (provider) {
      case 'openai':
        this.providerManager.registerProvider(provider, new OpenAIProviderAdapter(providerConfig))
        break

      case 'anthropic':
        this.providerManager.registerProvider(provider, new AnthropicProviderAdapter(providerConfig))
        break

      case 'deepseek':
        this.providerManager.registerProvider(provider, new DeepSeekProviderAdapter(providerConfig))
        break

      case 'local':
        if (!baseURL) {
          console.warn('Local models require baseURL configuration (e.g., http://localhost:11434)')
          return
        }
        this.providerManager.registerProvider(provider, new LocalProviderAdapter(providerConfig))
        break

      default:
        throw new Error(`Unsupported AI provider: ${provider}`)
    }

    this.providerManager.setDefaultProvider(provider)
    console.log(`Initialized ${provider} provider with model: ${providerConfig.model}`)
  }

  private getDefaultModel(provider: AIProvider): string {
    const defaultModels: Record<AIProvider, string> = {
      'openai': 'gpt-3.5-turbo',
      'anthropic': 'claude-3-5-sonnet-20241022',
      'deepseek': 'deepseek-chat',
      'local': 'llama2'
    }
    return defaultModels[provider]
  }

  private initializeChains(): void {
    // Note: We're moving away from chains to direct provider calls for better flexibility
    console.log('Chain-based processing deprecated - using direct provider calls')
  }

  async updateConfig(newConfig: Partial<LangChainConfig>): Promise<void> {
    this.config = { ...this.config, ...newConfig }

    // Save to store
    Object.entries(newConfig).forEach(([key, value]) => {
      this.store.set(`ai.${key}`, value)
    })

    // Reinitialize everything with new config
    this.isInitialized = false
    await this.initialize()
  }

  getConfig(): LangChainConfig {
    return { ...this.config }
  }

  isConfigured(): boolean {
    try {
      const provider = this.providerManager.getProvider(this.config.provider)
      return !!provider && (!!this.config.apiKey || this.config.provider === 'local')
    } catch (error) {
      return false
    }
  }

  private async executeAction(action: string, input: any, options: AIRequestOptions = {}): Promise<string> {
    // Ensure service is initialized
    if (!this.isInitialized) {
      await this.initialize()
    }

    if (!this.isConfigured()) {
      throw new Error('AI服务未配置，请在设置中配置API密钥')
    }

    // Generate cache key
    const cacheKey = this.generateCacheKey(action, input, options)

    // Check cache first
    const cachedResult = this.cache.get(cacheKey)
    if (cachedResult) {
      console.log(`Cache hit for action: ${action}`)
      return cachedResult
    }

    // Build messages
    const messages = this.buildMessages(action, input)

    // Set up request options
    const requestOptions: AIRequestOptions = {
      temperature: options.temperature || this.config.temperature,
      maxTokens: options.maxTokens || this.config.maxTokens,
      timeout: options.timeout || this.config.timeout,
      retries: options.retries || this.config.retries,
    }

    const maxRetries = this.config.retries || 3
    let lastError: Error | null = null

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        const response = await this.providerManager.generateResponse(
          messages,
          this.config.provider,
          requestOptions
        )

        const result = response.content.trim()

        // Cache the result
        this.cache.set(cacheKey, result, 1800) // Cache for 30 minutes

        // Log token usage
        if (response.usage) {
          console.log(`Token usage for ${action}:`, {
            prompt: response.usage.promptTokens,
            completion: response.usage.completionTokens,
            total: response.usage.totalTokens,
            cost: response.usage.cost ? `$${response.usage.cost.toFixed(4)}` : 'N/A'
          })
        }

        return result
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error))
        console.error(`AI操作失败 (尝试 ${attempt + 1}/${maxRetries + 1}):`, lastError)

        // Don't retry on configuration errors
        if (this.isNonRetryableError(lastError)) {
          throw lastError
        }

        if (attempt < maxRetries) {
          // Exponential backoff
          const delay = Math.min(1000 * Math.pow(2, attempt), 5000)
          console.log(`等待 ${delay}ms 后重试...`)
          await new Promise(resolve => setTimeout(resolve, delay))
        }
      }
    }

    throw new Error(`AI处理失败: ${lastError?.message || '未知错误'}`)
  }

  private buildMessages(action: string, input: any): BaseMessage[] {
    const systemPrompts: Record<string, string> = {
      polish: "你是一个专业的写作助手，请润色以下文本，使其更加流畅、专业和优雅。保持原意不变，只优化表达方式、语法结构和用词选择。",
      shorten: "请将以下文本缩短到原来长度的30-50%，提炼核心观点和关键信息，保持逻辑完整和表达清晰。",
      expand: "请对以下文本进行扩写，添加更多细节、背景信息和深入分析，使内容更加丰富和完整。保持原文的核心观点和逻辑结构。",
      translate: "你是一个专业的翻译助手，请将以下文本准确翻译成{target_lang}，保持原文的语气、风格和专业性。",
      fixGrammar: "请检查并修正以下文本中的语法错误、拼写错误和标点符号错误，保持原文的意思和风格不变。如果文本没有错误，请返回原文。",
      changeTone: "请将以下文本的语气改为{tone}，保持内容的核心意思不变，只调整表达方式、用词选择和句式结构。",
      summarize: "请为以下文本生成一个简洁明了的摘要，突出主要观点和关键信息，长度控制在原文的20-30%左右。",
      continueWriting: "请基于以下上下文智能续写内容，保持风格一致、逻辑连贯，自然地延伸和发展现有内容。续写内容应该与上下文高度相关且有价值。"
    }

    const systemPrompt = systemPrompts[action] || "你是一个AI写作助手，请根据用户需求提供帮助。"
    let userPrompt = ""

    switch (action) {
      case 'translate':
        userPrompt = systemPrompt.replace('{target_lang}', input.target_lang || 'English') + `\n\n请翻译以下文本：\n${input.text}`
        break
      case 'changeTone':
        userPrompt = systemPrompt.replace('{tone}', input.tone || 'professional') + `\n\n请调整以下文本的语气：\n${input.text}`
        break
      case 'continueWriting':
        userPrompt = systemPrompt + `\n\n上下文：${input.context || '无特定上下文'}\n\n当前文本：${input.text}\n\n请续写：`
        break
      default:
        userPrompt = systemPrompt + `\n\n${input.text}`
        break
    }

    return [
      new SystemMessage(systemPrompt),
      new HumanMessage(userPrompt)
    ]
  }

  private generateCacheKey(action: string, input: any, options: AIRequestOptions): string {
    const keyData = {
      action,
      input,
      provider: this.config.provider,
      model: this.config.model,
      temperature: options.temperature || this.config.temperature,
      maxTokens: options.maxTokens || this.config.maxTokens
    }
    return CacheManager.generateKeyFromObject(keyData)
  }

  private isNonRetryableError(error: Error): boolean {
    const message = error.message.toLowerCase()
    return (
      message.includes('api key') ||
      message.includes('authentication') ||
      message.includes('authorization') ||
      message.includes('invalid') ||
      message.includes('not found')
    )
  }

  async *streamAIAction(action: string, input: any, options: AIStreamOptions = {}): AsyncGenerator<string, void, unknown> {
    // Ensure service is initialized
    if (!this.isInitialized) {
      await this.initialize()
    }

    if (!this.isConfigured()) {
      throw new Error('AI服务未配置，请在设置中配置API密钥')
    }

    // Build messages
    const messages = this.buildMessages(action, input)

    // Set up request options
    const requestOptions: AIRequestOptions = {
      temperature: options.temperature || this.config.temperature,
      maxTokens: options.maxTokens || this.config.maxTokens,
      timeout: options.timeout || this.config.timeout,
      retries: options.retries || this.config.retries,
      stream: true,
    }

    try {
      const stream = this.providerManager.generateStream(
        messages,
        this.config.provider,
        requestOptions
      )

      let fullResponse = ''

      for await (const chunk of stream) {
        fullResponse += chunk
        yield chunk

        // Call progress callback if provided
        if (options.onProgress) {
          options.onProgress(chunk)
        }
      }

      // Cache the complete result
      const cacheKey = this.generateCacheKey(action, input, options)
      this.cache.set(cacheKey, fullResponse, 1800) // Cache for 30 minutes

      // Call completion callback if provided
      if (options.onComplete) {
        options.onComplete(fullResponse)
      }

      console.log(`Streaming completed for ${action}, total length: ${fullResponse.length}`)
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error))
      console.error(`AI流式处理失败:`, err)

      // Call error callback if provided
      if (options.onError) {
        options.onError(err)
      }

      throw new Error(`AI流式处理失败: ${err.message}`)
    }
  }

  // Utility method to create direct chat completion (for more complex scenarios)
  async chatCompletion(messages: BaseMessage[], options: AIRequestOptions = {}): Promise<string> {
    // Ensure service is initialized
    if (!this.isInitialized) {
      await this.initialize()
    }

    if (!this.isConfigured()) {
      throw new Error('AI服务未配置，请在设置中配置API密钥')
    }

    if (!this.llm) {
      throw new Error('LLM未初始化')
    }

    try {
      const result = await this.llm.invoke(messages, {
        temperature: options.temperature || this.config.temperature,
        maxTokens: options.maxTokens || this.config.maxTokens,
        timeout: options.timeout || this.config.timeout,
      })

      return result.content as string
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error))
      console.error('Chat completion failed:', err)
      throw new Error(`聊天完成失败: ${err.message}`)
    }
  }

  // Streaming chat completion
  async *streamChatCompletion(messages: BaseMessage[], options: AIStreamOptions = {}): AsyncGenerator<string, void, unknown> {
    // Ensure service is initialized
    if (!this.isInitialized) {
      await this.initialize()
    }

    if (!this.isConfigured()) {
      throw new Error('AI服务未配置，请在设置中配置API密钥')
    }

    if (!this.llm) {
      throw new Error('LLM未初始化')
    }

    try {
      const stream = await this.llm.stream(messages, {
        temperature: options.temperature || this.config.temperature,
        maxTokens: options.maxTokens || this.config.maxTokens,
        timeout: options.timeout || this.config.timeout,
      })

      let fullResponse = ''

      for await (const chunk of stream) {
        const content = chunk.content as string
        fullResponse += content
        yield content

        if (options.onProgress) {
          options.onProgress(content)
        }
      }

      if (options.onComplete) {
        options.onComplete(fullResponse)
      }
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error))
      console.error('Streaming chat completion failed:', err)

      if (options.onError) {
        options.onError(err)
      }

      throw new Error(`流式聊天完成失败: ${err.message}`)
    }
  }

  // AI Action implementations
  async polish(text: string): Promise<string> {
    return this.executeAction('polish', { text })
  }

  async shorten(text: string): Promise<string> {
    return this.executeAction('shorten', { text })
  }

  async expand(text: string): Promise<string> {
    return this.executeAction('expand', { text })
  }

  async translate(text: string, targetLang: string = 'English'): Promise<string> {
    return this.executeAction('translate', { text, target_lang: targetLang })
  }

  async fixGrammar(text: string): Promise<string> {
    return this.executeAction('fixGrammar', { text })
  }

  async changeTone(text: string, tone: string): Promise<string> {
    return this.executeAction('changeTone', { text, tone })
  }

  async summarize(text: string): Promise<string> {
    return this.executeAction('summarize', { text })
  }

  async continueWriting(text: string, context: string = ''): Promise<string> {
    return this.executeAction('continueWriting', { text, context })
  }

  // Test connection
  async testConnection(): Promise<boolean> {
    try {
      if (!this.isConfigured()) {
        return false
      }

      const result = await this.executeAction('polish', { text: 'Hello world' })
      return !!result
    } catch (error) {
      console.error('AI connection test failed:', error)
      return false
    }
  }

  // Get cache statistics
  public getCacheStats() {
    return this.cache.getStats()
  }

  // Clear cache
  public clearCache(): void {
    this.cache.clear()
    console.log('AI response cache cleared')
  }

  // Get token usage estimation
  public estimateTokens(text: string, operation?: string): number {
    // Add system prompt tokens for the operation
    let systemTokens = 50 // Base system prompt
    if (operation) {
      systemTokens += this.tokenManager.countTokens(
        this.buildMessages(operation, { text })[0].content as string
      )
    }

    const textTokens = this.tokenManager.countTokens(text, this.config.model)
    return systemTokens + textTokens
  }

  // Get available providers
  public getAvailableProviders(): string[] {
    return this.providerManager.listProviders()
  }

  // Add new provider dynamically
  public async addProvider(name: string, config: ProviderConfig): Promise<void> {
    const {
      OpenAIProviderAdapter,
      AnthropicProviderAdapter,
      DeepSeekProviderAdapter,
      LocalProviderAdapter
    } = await import('./AIProviderAdapter')

    let adapter
    switch (config.apiKey?.includes('openai') ? 'openai' :
            config.apiKey?.includes('anthropic') ? 'anthropic' :
            config.baseURL?.includes('deepseek') ? 'deepseek' :
            config.baseURL?.includes('localhost') ? 'local' : 'openai') {
      case 'openai':
        adapter = new OpenAIProviderAdapter(config)
        break
      case 'anthropic':
        adapter = new AnthropicProviderAdapter(config)
        break
      case 'deepseek':
        adapter = new DeepSeekProviderAdapter(config)
        break
      case 'local':
        adapter = new LocalProviderAdapter(config)
        break
      default:
        throw new Error(`Unsupported provider type for: ${name}`)
    }

    this.providerManager.registerProvider(name, adapter)
    console.log(`Added new provider: ${name}`)
  }

  /**
   * 安全存储API密钥
   */
  public async storeApiKey(provider: AIProvider, apiKey: string): Promise<void> {
    try {
      await this.securityService.storeApiKey(provider, apiKey)

      // 更新当前配置
      if (this.config.provider === provider) {
        this.config.apiKey = apiKey
        this.isInitialized = false
        await this.initialize()
      }

      console.log(`API key for ${provider} stored securely`)
    } catch (error) {
      console.error(`Failed to store API key for ${provider}:`, error)
      throw error
    }
  }

  /**
   * 获取安全存储的API密钥
   */
  public async getSecureApiKey(provider?: AIProvider): Promise<string | null> {
    try {
      const targetProvider = provider || this.config.provider
      return await this.securityService.getApiKey(targetProvider)
    } catch (error) {
      console.error(`Failed to get API key for ${provider || this.config.provider}:`, error)
      return null
    }
  }

  /**
   * 删除API密钥
   */
  public async deleteApiKey(provider: AIProvider): Promise<void> {
    try {
      await this.securityService.deleteApiKey(provider)

      // 如果删除的是当前提供商的密钥，更新配置
      if (this.config.provider === provider) {
        this.config.apiKey = ''
        this.isInitialized = false
      }

      console.log(`API key for ${provider} deleted securely`)
    } catch (error) {
      console.error(`Failed to delete API key for ${provider}:`, error)
      throw error
    }
  }

  /**
   * 获取所有已存储的提供商
   */
  public async getStoredProviders(): Promise<AIProvider[]> {
    // 这里需要实现获取所有已存储提供商的逻辑
    // 由于SecurityService的限制，暂时返回空数组
    return []
  }

  /**
   * 验证API密钥
   */
  public async validateApiKey(provider: AIProvider, apiKey: string): Promise<boolean> {
    try {
      // 创建临时配置来测试API密钥
      const tempConfig: ProviderConfig = {
        apiKey,
        model: this.getDefaultModel(provider),
        temperature: 0.7,
        maxTokens: 100,
        timeout: 10000,
        retries: 1
      }

      const tempProviderManager = new AIProviderManager()

      // 导入适配器类
      const {
        OpenAIProviderAdapter,
        AnthropicProviderAdapter,
        DeepSeekProviderAdapter,
        LocalProviderAdapter
      } = await import('./AIProviderAdapter')

      let adapter
      switch (provider) {
        case 'openai':
          adapter = new OpenAIProviderAdapter(tempConfig)
          break
        case 'anthropic':
          adapter = new AnthropicProviderAdapter(tempConfig)
          break
        case 'deepseek':
          adapter = new DeepSeekProviderAdapter(tempConfig)
          break
        case 'local':
          adapter = new LocalProviderAdapter(tempConfig)
          break
        default:
          return false
      }

      tempProviderManager.registerProvider(provider, adapter)

      // 测试简单的请求
      const testMessages = [
        new SystemMessage('You are a helpful assistant.'),
        new HumanMessage('Say "Hello"')
      ]

      const response = await tempProviderManager.generateResponse(testMessages, provider, {
        maxTokens: 10,
        timeout: 10000,
        retries: 1
      })

      return response.content && response.content.length > 0
    } catch (error) {
      console.error(`API key validation failed for ${provider}:`, error)
      return false
    }
  }

  // Destroy service and clean up resources
  public destroy(): void {
    this.cache.destroy()
    console.log('LangChainService destroyed')
  }

  // Get available models
  getAvailableModels(): string[] {
    switch (this.config.provider) {
      case 'openai':
        return [
          'gpt-3.5-turbo',
          'gpt-4',
          'gpt-4-turbo-preview',
          'gpt-4o',
          'gpt-4o-mini'
        ]
      case 'anthropic':
        return [
          'claude-3-haiku-20240307',
          'claude-3-sonnet-20240229',
          'claude-3-opus-20240229',
          'claude-3-5-sonnet-20241022',
          'claude-3-5-haiku-20241022'
        ]
      case 'deepseek':
        return [
          'deepseek-chat',
          'deepseek-coder'
        ]
      case 'local':
        return [
          'llama2',
          'llama2:13b',
          'llama2:70b',
          'codellama',
          'mistral',
          'vicuna'
        ]
      default:
        return []
    }
  }

  // Get supported providers
  static getSupportedProviders(): { value: AIProvider; label: string; description: string }[] {
    return [
      {
        value: 'openai',
        label: 'OpenAI',
        description: 'GPT系列模型，强大的通用AI助手'
      },
      {
        value: 'anthropic',
        label: 'Anthropic',
        description: 'Claude系列模型，擅长写作和推理'
      },
      {
        value: 'deepseek',
        label: 'DeepSeek',
        description: '国产大模型，性价比高'
      },
      {
        value: 'local',
        label: '本地模型',
        description: '使用Ollama等本地部署的模型'
      }
    ]
  }

  // Validate configuration
  validateConfig(config: Partial<LangChainConfig>): { isValid: boolean; errors: string[] } {
    const errors: string[] = []

    if (config.provider && !['openai', 'anthropic', 'deepseek', 'local'].includes(config.provider)) {
      errors.push('不支持的AI提供商')
    }

    if (config.provider !== 'local' && !config.apiKey && !this.config.apiKey) {
      errors.push('API密钥未配置')
    }

    if (config.baseURL && !this.isValidURL(config.baseURL)) {
      errors.push('无效的API基础URL')
    }

    if (config.temperature !== undefined && (config.temperature < 0 || config.temperature > 2)) {
      errors.push('温度参数必须在0-2之间')
    }

    if (config.maxTokens !== undefined && config.maxTokens <= 0) {
      errors.push('最大token数必须大于0')
    }

    return {
      isValid: errors.length === 0,
      errors
    }
  }

  private isValidURL(string: string): boolean {
    try {
      new URL(string)
      return true
    } catch (_) {
      return false
    }
  }

  // Health check
  async healthCheck(): Promise<{ isHealthy: boolean; message: string; latency?: number }> {
    if (!this.isInitialized) {
      await this.initialize()
    }

    if (!this.isConfigured()) {
      return {
        isHealthy: false,
        message: 'AI服务未配置'
      }
    }

    const startTime = Date.now()
    try {
      const testText = 'Hello world'
      const result = await this.executeAction('polish', { text: testText })
      const latency = Date.now() - startTime

      return {
        isHealthy: true,
        message: 'AI服务运行正常',
        latency
      }
    } catch (error) {
      const latency = Date.now() - startTime
      return {
        isHealthy: false,
        message: `AI服务异常: ${error instanceof Error ? error.message : '未知错误'}`,
        latency
      }
    }
  }
}